local oo = require("SharedLibs.BaseLib")
local co = require("SharedLibs.CoroLib")

Debugger = require("SharedLibs.Debugger")

SignalWrapper = require("SharedLibs.Signal.SignalWrapper")
SignalMerger = require("SharedLibs.Signal.SignalMerger")

WaitSignal = require("SharedLibs.Signal.WaitSignal")
TimerMgr = require("SharedLibs.TimerMgr")

local m = {}

--[[
扩展Signal的模版:

cls.XXXWrap = function(self, func, ...) 
	local aaa = ...;
	...

	return SignalWrapper.new(self, function(val)
		if func(aaa, val) then
			aaa = val;
			return true, val;
		else
			return false;
		end					
	end);
end	
]]

--------
function m.Extend1(cls)	
	function cls:Merge(...)
		return SignalMerger.new(self, ...);
	end	

	--- [Block] 
	--- retTuple: 是否直接把signal的varReturn返回为tuple(table)而不是...
	function cls:Wait(predicate, retTuple)
		--- 注意: 这里不能通过 local co = co.GetCurrent(), 判断co为nil就用RawWait
		--- 因为当一个Co在Co内部被停止时, GetCurrent()也是nil,这时是不应该调用RawWait的!!!
		return co.yield(oo.New(WaitSignal, self, predicate, retTuple));
	end

	---
	function cls:NewWait(predicate)
		return oo.New(WaitSignal, self, predicate);
	end

	--- 让Signal可以在Roblox原生Coro里使用Signal:RawWait()进行等待
	function cls:RawWait(predicate)
		if Debugger.developMode then
			assert(TimerMgr._InTicking == false, "Should not call Signal:RawWait in ticked event!");
			Debugger.Assert(co.GetCurrent() == nil, "You cannot use Signal:RawWait in a Coro! May cause unexpected error!");
		end

		local co = coroutine.running();
		local handler = nil;
		handler = function(...)
			if predicate then
				if predicate(...) then
					self:Off(handler);
					--- 注意：这里不能用coroutine.resume(co, ...), 而必须用task.spawn，否则RPC里调用RawWait会导致RPC无法返回！
					task.spawn(co, ...);
				end
			else
				self:Off(handler);
				task.spawn(co, ...);
			end
		end;	
		self:On(handler)
		return coroutine.yield();
	end

	--- Wrap current signal into a new signal. Return the new wrapped signal.
	--- wrapperFunc接收任意参数，返回: ret, ...
	--- 如果第一个返回值是false，则这个data被过滤掉。否则，将Emit(...)
	function cls:Wrap(wrapperFunc, name)
		return SignalWrapper.new(self, wrapperFunc, name);
	end

	--- Takes every item your Signal and transform it to another value
	function cls:Map(mapper, name)
		return SignalWrapper.new(self, function(...) 
			return true, mapper(...);
		end, name);
	end		

	--- Takes every item your Signal and transform it to specified value
	function cls:MapTo(value, name)
		return SignalWrapper.new(self, function(...)
			return true, value;
		end, name);
	end

	--- Use predicator to filter signal values
	function cls:Filter(predicator, name)
		return SignalWrapper.new(self, function(...)
			if predicator(...) then return true, ...;
			else
				return false;
			end
		end, name);
	end	

	--[[
 	去抖动.
 	详见: https://reactivex.io/documentation/operators/debounce.html
 	
 	如果 oMarked 为false，那么就是普通的去抖（只有有效时会输出）
 	如果 oMarked 为true，那么Debounce后的Signal任何
 	时候都会输出信号。但是：
		有效时输出 true, ...
		无效时输出 false, ...
	]]	 
	function cls:Debounce(interval, oMarked)
		assert(interval > 0)

		local cached = nil;
		local timer = nil;

		local newSignal = nil;
		newSignal = SignalWrapper.new(self, function(...)
			cached = {...};
			if timer then timer:Cancel() end

			timer = TimerMgr.SetTimer(interval, function() 
				newSignal:Emit(table.unpack(cached));
				cached = nil;
				timer = nil;
			end)			
		end);
		return newSignal;
	end

	function cls:BufferedDebounce(interval, oMarked)
		assert(interval > 0)

		local buffered = {};
		local timer = nil;

		local newSignal = nil;
		newSignal = SignalWrapper.new(self, function(...)
			table.insert(buffered, {...});
			if timer then timer:Cancel() end

			timer = TimerMgr.SetTimer(interval, function()
				newSignal:Emit(buffered);
				buffered = {};
				timer = nil;
			end)			
		end);
		return newSignal;
	end	
	
	--[[
	每触发一次就会过热,然后需要等待interval后,才能继续触发	
		如果 oMarked 为false，那么只有有效时会输出
		如果 oMarked 为true，那么 Cooldown 后的Signal任何
		时候都会输出信号。但是：
		有效时输出 true, ...
		无效时输出 false, ...	
	]] 
	function cls:Cooldown(interval, oMarked)
		if interval == 0 then
			return self;
		else
			local isValid = true;

			return SignalWrapper.new(self, function(...)			
				if isValid then
					isValid = false;
					TimerMgr.SetTimer(interval, function() 
						isValid = true;
					end)
					if oMarked then
						return true, true, ...;
					else						
						return true, ...;
					end
				else					
					if oMarked then
						return true, false, ...;
					else						
						return false;
					end					
				end
			end);
		end
	end	

	--[[
	节流
		如果 oMarked 为false，那么就是普通的节流（只有有效时会输出）
		如果 oMarked 为true，那么 Throttle 后的Signal任何
		时候都会输出信号。但是：
		有效时输出 true, ...
		无效时输出 false, ...	
	]]
	function cls:Throttle(interval, oMarked)
		if interval == 0 then
			return self;
		else
			local isValid = true;
			return SignalWrapper.new(self, function(...)
				if isValid then
					isValid = false;
					TimerMgr.SetTimer(interval, function() 
						isValid = true;
					end)
					if oMarked then
						return true, true, ...;
					else
						return true, ...;						
					end
				else
					if oMarked then
						return true, false, ...;
					else
						return false;
					end
				end
			end);
		end
	end

	--[[
		和最后一次Emit的数值比较, 如果满足predicator就Emit.
		只支持单数据的Signal
	]]
	function cls:Compare2LastEmit(compareFunc)
		local last = nil;

		return SignalWrapper.new(self, function(val)
			if last == nil then
				last = val;
				return true, val;
			else
				if compareFunc(last, val) then
					last = val;
					return true, val;
				else
					return false;
				end		
			end			
		end);
	end	

	----
	function cls:Buffer(count)	
		local curCount = 0;
		return SignalWrapper.new(self, function(...)
			curCount = curCount + 1;
			if curCount >= count then
				curCount = 0;
				return true, ...;
			else
				return false;
			end
		end);
	end
end


return m;
